package admin

import (
	"config-client-v1/init/global"
	j_log "config-client-v1/init/j-log"
	"config-client-v1/model"
	"config-client-v1/utils/tools"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"gopkg.in/ini.v1"
	"gorm.io/gorm"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
)

type ProjectController struct {
}

func (con ProjectController) List(ctx *gin.Context) {
	param := struct {
		Name string `json:"name"`
		model.QueryData
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	tx := model.DB.Where("del = 0")
	if param.Name != "" {
		tx.Where("name like ?", "%"+param.Name+"%")
	}
	var count int64
	list := make([]*model.Project, 0)
	err = tx.Model(&model.Project{}).Count(&count).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	err = tx.Preload("UserInfo", func(tx2 *gorm.DB) *gorm.DB {
		return tx2.Where("role = 0")
	}).Order("id desc").Offset(param.StartPage()).Limit(param.PageSize).Find(&list).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	for _, row := range list {
		if row.UserInfo == nil {
			row.UserInfo = &model.UserSlim{}
		}
		if row.ID == model.RunningProject.ID {

			list1 := make([]*model.FSEquip, 0)
			_ = model.DB.Where("project_id = ?", row.ID).Find(&list1).Error
			list2 := make([]*model.PPE, 0)
			_ = model.DB.Where("project_id = ?", row.ID).Find(&list2).Error

			fseNum := 0
			ppeNum := 0
			startedNum := 0
			stoppedNum := 0
			restartingNum := 0

			for _, row2 := range list1 {
				fseNum++
				if row2.RunningState == 2 {
					startedNum++
				} else if row2.RunningState == 0 || row2.RunningState == 4 {
					stoppedNum++
				} else {
					restartingNum++
				}
			}

			for _, row2 := range list2 {
				ppeNum++
				if row2.RunningState == 2 {
					startedNum++
				} else if row2.RunningState == 0 || row2.RunningState == 4 {
					stoppedNum++
				} else {
					restartingNum++
				}
			}

			row.RunningFSENum = fseNum
			row.RunningPPENum = ppeNum
			row.RunningTotalNum = fseNum + ppeNum
			row.StartedNum = startedNum
			row.StoppedNum = stoppedNum
			row.RestartingNum = restartingNum

			//model.RunningFSMap.RLock()
			//row.RunningFSENum = len(model.RunningFSMap.M)
			//model.RunningFSMap.RUnlock()
			//
			//model.RunningPPEMap.RLock()
			//row.RunningPPENum = len(model.RunningPPEMap.M)
			//model.RunningPPEMap.RUnlock()
			//row.RunningTotalNum = row.RunningFSENum + row.RunningPPENum
		}
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"list":  list,
			"count": count,
		},
	})
}

func (con ProjectController) Save(ctx *gin.Context) {
	param := struct {
		Id       int    `json:"id"`
		Name     string `json:"name" binding:"required,max=30"`
		Desc     string `json:"desc" binding:"max=255"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数" + err.Error(),
			"data":    "",
		})
		return
	}
	data := model.Project{}
	data.Name = param.Name
	data.Desc = param.Desc
	now := tools.GetNow()
	if param.Id == 0 {
		// 创建工程必创建新用户, 密码默认123456, 工程的第一个用户为管理员账户
		data.CreateTime = &now
		data.UpdateTime = &now
		err = model.DB.Omit("PPEList", "UserList").Create(&data).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
			})
			return
		}
		user := &model.User{}
		user.ProjectID = data.ID
		user.Role = 0
		user.Username = param.Username
		user.Password = tools.Md5("123456" + LOGIN_KEY)
		user.Status = 1
		user.CreateTime = &now
		user.UpdateTime = &now
		err = model.DB.Create(&user).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    7,
				"message": err.Error(),
			})
			return
		}
		err = model.DB.Model(&model.Project{}).Where("id = ?", data.ID).Update("uid", user.Id).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    8,
				"message": err.Error(),
			})
			return
		}

		err = ProjectCreatedHook(data.ID)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": "创建默认报警信息失败",
				"data": map[string]interface{}{
					"err": err.Error(),
				},
			})
			return
		}
	} else {
		// 修改工程只需校验工程密码
		if param.Password == "" {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "密码不能为空",
			})
			return
		}
		project := &model.Project{}
		projectExist, _ := project.Exists(param.Id)
		if !projectExist {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的工程ID",
			})
			return
		}

		user := &model.User{}
		err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.Id, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
			})
			return
		}

		if user.Id == 0 { // 账号密码不存在
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "工程账号密码不匹配",
			})
			return
		}

		data.UpdateTime = &now
		err = model.DB.Model(&model.Project{}).Omit("PPEList", "UserList").Where("id = ?", param.Id).Select("name", "desc", "update_time").Updates(&data).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    7,
				"message": err.Error(),
			})
			return
		}
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con ProjectController) ServerConfig(ctx *gin.Context) {
	param := &model.Project{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	if param.ServerName == "" {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "主服务器名称不可为空",
		})
		return
	}
	if param.ServerIP == "" {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "主服务器IP不可为空",
		})
		return
	}
	if !tools.IsValidIP(param.ServerIP) {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的IP地址",
		})
		return
	}
	if param.RedundantEnable == 1 {
		if !tools.IsValidIP(param.RedundantIP) {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的冗余IP地址",
			})
			return
		}
	}
	projectExist := &model.Project{}
	exist, err := projectExist.Exists(param.ID)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if !exist {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}
	now := tools.GetNow()
	param.UpdateTime = &now
	err = model.DB.Model(&model.Project{}).Where("id = ?", param.ID).
		Select("server_name", "server_ip", "server_port",
			"redundant_enable", "redundant_name", "redundant_ip",
			"redundant_port", "redundant_time_ss", "time_ss").Updates(&param).Error

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con ProjectController) Enter(ctx *gin.Context) {
	param := struct {
		Id       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	// 修改工程只需校验工程密码

	user := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.Id, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	if user.Id == 0 { // 账号密码不存在
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程账号密码不匹配",
		})
		return
	}
	info := model.Project{}
	err = model.DB.Model(&model.Project{}).Where("id = ?", param.Id).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"info": info,
		},
	})
}

func (con ProjectController) Detail(ctx *gin.Context) {
	param := struct {
		Id int `json:"id" binding:"required"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	info := model.Project{}
	err = model.DB.Model(&model.Project{}).Where("id = ?", param.Id).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"info": info,
		},
	})
}

func (con ProjectController) Delete(ctx *gin.Context) {
	param := struct {
		ID       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	user := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.ID, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	//if user.Id == 0 { // 账号密码不存在 todo todo
	//	ctx.JSON(200, gin.H{
	//		"code":    2,
	//		"message": "工程账号密码不匹配",
	//	})
	//	return
	//}
	//if user.Role != 0 {
	//	ctx.JSON(200, gin.H{
	//		"code":    2,
	//		"message": "只有工程管理员才能删除工程",
	//	})
	//	return
	//}

	info := model.Project{}
	err = model.DB.Model(&model.Project{}).Where("id = ?", param.ID).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	err = model.DeleteProject(param.ID)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    10,
			"message": err.Error(),
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con ProjectController) Start(ctx *gin.Context) {
	param := struct {
		ID       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	project := model.Project{}
	err = model.DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Where("id = ? AND del = 0", param.ID).Limit(1).Find(&project).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	user := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.ID, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if user.Id == 0 { // 账号密码不存在
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程账号密码不匹配",
		})
		return
	}

	if model.RunningProject.ID != 0 {
		if model.RunningProject.ID == param.ID {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "工程正在运行中",
			})
			return
		}
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "其他工程正在运行中",
		})
		return
	}
	err = model.ProjectStartHook(param.ID)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": err.Error(),
		})
		return
	}

	for _, ppe := range project.PPEList {
		go func(ppe *model.PPE) {
			for _, frame := range ppe.FrameList {
				frame.JsonDetail, _ = model.GetLatestFrameJsonDetail(frame)
			}
			err = ppe.Start()
			if err != nil {
				j_log.LogError([]interface{}{fmt.Sprintf("第三方设备【%s】启动失败 %s", ppe.Name, err.Error())})
				return
			}
		}(ppe)
	}

	for _, fse := range project.FSEList {
		go func(fse *model.FSEquip) {
			fseExist, ok := model.RunningFSMap.Get(fse.ID)
			if ok && fseExist.RunningState != 4 {
				j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】运行中, 不可重复启动", fse.Name)})
				return
			}

			fse.CalculateDataScale()
			model.RunningFSMap.Set(fse.ID, fse)
			fse.RunningState = 1 // 正在启动
			_ = fse.UpdateRunningState(1, "正在启动")
			go fse.RunFSTcpClient()
			startTime := time.Now()
			for {
				time.Sleep(100 * time.Millisecond)
				if fse.RunningState == 2 {
					break
				}
				if time.Since(startTime) > time.Second*5 {
					j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】启动超时", fse.Name)})
					_ = model.SysAlertSave(&model.SysAlert{
						ProjectID:  fse.ProjectID,
						Const:      "ALERT_FS",
						Alert:      "自研设备启动超时",
						RelationID: fse.ID,
					})
					return
				}
			}
			if fse.Stage == 1 {
				go func(fse *model.FSEquip) {
					time.Sleep(1 * time.Second)
					data := model.GenFSFrameData0601()
					_, err = fse.Write(data)
					if err != nil {
						j_log.LogError([]interface{}{"首次握手tcp发送失败", fse.Name, fse.IP, fse.Port, err.Error()})
						return
					}
					j_log.LogInfo([]interface{}{"首次握手tcp发送成功", fse.Name, fse.IP, fse.Port})
				}(fse)
			}
		}(fse)
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con ProjectController) Stop(ctx *gin.Context) {
	param := struct {
		ID       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	project := model.Project{}
	err = model.DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Where("id = ? AND del = 0", param.ID).Limit(1).Find(&project).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	user := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.ID, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if user.Id == 0 { // 账号密码不存在
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程账号密码不匹配",
		})
		return
	}

	if model.RunningProject.ID != 0 && model.RunningProject.ID != param.ID {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "当前工程未运行",
		})
		return
	}

	for _, row := range project.PPEList {
		go func(param *model.PPE) {
			ppe, ok := model.RunningPPEMap.Get(param.ID)
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("第三方设备【%s】未运行", param.Name)})
				return
			}
			err = ppe.Stop()
			if err != nil {
				j_log.LogError([]interface{}{fmt.Sprintf("第三方设备【%s】停止失败 %s", ppe.Name, err.Error())})
				return
			}
		}(row)
	}

	for _, row := range project.FSEList {
		go func(param *model.FSEquip) {
			fse, ok := model.RunningFSMap.Get(param.ID)
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】 未运行", param.Name)})
				fse = &model.FSEquip{ID: param.ID}
				return
			}

			fse.RunningState = 3
			_ = fse.UpdateRunningState(3, "正在停止")
			if fse.TcpClient != nil && fse.TcpClient.Connection != nil {
				_ = fse.TcpClient.Connection.Close()
			}
			startTime := time.Now()
			for {
				time.Sleep(100 * time.Millisecond)
				if fse.RunningState == 4 {
					break
				}
				if time.Since(startTime) > time.Second*10 {
					j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】 停止超时", param.Name)})
					return
				}
			}
		}(row)
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

// RunningEquipInfo 运行中的设备信息
func (con ProjectController) RunningEquipInfo(ctx *gin.Context) {
	//model.RunningPPEMap.RLock()
	//ppeList := make([]*model.PPE, 0)
	//for _, v := range model.RunningPPEMap.M {
	//	ppeList = append(ppeList, &model.PPE{
	//		ID:           v.ID,
	//		ProjectID:    v.ProjectID,
	//		Code1:        v.Code1,
	//		Code2:        v.Code2,
	//		Code3:        v.Code3,
	//		ProtocolName: v.ProtocolName,
	//		Name:         v.Name,
	//		Config:       v.Config,
	//		RunningState: v.RunningState,
	//		Detail:       v.Detail,
	//	})
	//}
	//model.RunningPPEMap.RUnlock()
	//sort.Slice(ppeList, func(i, j int) bool {
	//	return ppeList[i].ID < ppeList[j].ID
	//})
	//
	//model.RunningPPEMap.RLock()
	//
	//fseList := make([]*model.FSEquip, 0)
	//for _, v := range model.RunningFSMap.M {
	//	fseList = append(fseList, &model.FSEquip{
	//		ID:              v.ID,
	//		ProjectID:       v.ProjectID,
	//		Name:            v.Name,
	//		Code1:           v.Code1,
	//		IP:              v.IP,
	//		Port:            v.Port,
	//		RedundantEnable: v.RedundantEnable,
	//		RedundantIP:     v.RedundantIP,
	//		RedundantPort:   v.RedundantPort,
	//		RedundantParam1: v.RedundantParam1,
	//		RedundantParam2: v.RedundantParam2,
	//		RedundantParam3: v.RedundantParam3,
	//		IntEndian:       v.IntEndian,
	//		FloatEndian:     v.FloatEndian,
	//		RunningState:    v.RunningState,
	//		Detail:          v.Detail,
	//	})
	//}
	//model.RunningPPEMap.RUnlock()
	//
	//sort.Slice(fseList, func(i, j int) bool {
	//	return fseList[i].ID < fseList[j].ID
	//})

	list1 := make([]*model.FSEquip, 0)
	_ = model.DB.Where("running_state != 0 AND running_state != 4").Find(&list1).Error
	list2 := make([]*model.PPE, 0)
	_ = model.DB.Where("running_state != 0 AND running_state != 4").Find(&list2).Error
	sort.Slice(list1, func(i, j int) bool {
		return list1[i].ID < list1[j].ID
	})
	sort.Slice(list2, func(i, j int) bool {
		return list2[i].ID < list2[j].ID
	})

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"fse_list": list1,
			"ppe_list": list2,
		},
	})
}

/*Export 导出工程文件 --- */
func (con ProjectController) Export(ctx *gin.Context) {
	param := struct {
		Id       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	ip, port := tools.GetIPPortFromHost(ctx.Request.Host)
	updateData := make(map[string]interface{})
	updateData["server_ip"] = ip
	updateData["server_port"] = port
	_ = model.DB.Model(&model.Project{}).Where("id = ?", param.Id).Updates(updateData).Error

	userExist := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.Id, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&userExist).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	if userExist.Id == 0 { // 账号密码不存在
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程账号密码不匹配",
		})
		return
	}

	project := model.ProjectExt{}
	err = model.DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			}).Preload("ModbusServerDB").Preload("OPCServerItem")
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Where("id = ? AND del = 0", param.Id).Limit(1).Find(&project).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	pageGroupList := make([]*model.PageGroup, 0)
	err = model.DB.Preload("PageList", func(db *gorm.DB) *gorm.DB {
		return db.Where("del = 0").Order("id ASC")
	}).Order("id ASC").Find(&pageGroupList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	userList := make([]*model.User, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&userList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	authRoleList := make([]*model.AuthRole, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&authRoleList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	authRoleList = append(authRoleList, &model.AuthRole{Id: 0})
	for _, role := range authRoleList {
		role.UserList = make([]*model.User, 0)
		for _, user := range userList {
			if user.Role == role.Id {
				role.UserList = append(role.UserList, user)
			}
		}
	}

	alertTypeList := make([]*model.AlertType, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&alertTypeList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	project.AuthRoleList = authRoleList
	project.PageGroupList = pageGroupList
	project.AlertTypeList = alertTypeList

	for _, ppe := range project.PPEList {
		if ppe.Code1 == "58" || ppe.Code1 == "61" {
			itemList := make([]*model.FrameItem, 0)
			err = model.DB.Where("ppe_id = ?", ppe.ID).Find(&itemList).Error
			if err != nil {
				ctx.JSON(200, gin.H{
					"code":    6,
					"message": err.Error(),
				})
				return
			}
			ppe.FrameItemList = itemList
		}
	}
	itemList := make([]*model.FrameItem, 0)
	err = model.DB.Where("project_id = ? AND code1 = ?", project.ID, "90").Find(&itemList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	project.FrameItemList = itemList

	project.ApiURL = fmt.Sprintf("http://%s:%d/", project.ServerIP, project.ServerPort)
	addr := global.AppConfig.Section("websocket").Key("addr").String()
	project.WsURL = fmt.Sprintf("ws://%s%s/echo", project.ServerIP, addr)
	jsonB, _ := json.MarshalIndent(&project, "", "  ")
	projectDirName := fmt.Sprintf("tmp/%s", tools.GetNano())
	zipFileName := projectDirName + ".zip"
	jsonFileURL := fmt.Sprintf("%s/project.zt", projectDirName)
	jsonDestFile := filepath.Join(global.StaticPath, jsonFileURL)
	err = tools.WriteStringToFile(string(jsonB), jsonDestFile, "w")
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "WriteStringToFile error" + err.Error(),
			"data":    "",
		})
		return
	}
	err = tools.CopyDir(filepath.Join(global.StaticPath, fmt.Sprintf("upload/audio/%d", project.ID)), filepath.Join(global.StaticPath, projectDirName, "audio"))
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "copyDir error" + err.Error(),
			"data":    "",
		})
		return
	}
	zipDestFile := filepath.Join(global.StaticPath, zipFileName)
	err = tools.ZipCompressWithoutTopLevel(filepath.Join(global.StaticPath, projectDirName), zipDestFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "zipCompress error " + err.Error(),
			"data":    "",
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"url":  zipFileName,
			"name": project.Name,
		},
	})
}

func (con ProjectController) ExportIn(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Id int `json:"id" binding:"required"`
	}{}
	param.Id = userToken.ProjectID
	project := model.ProjectExt{}
	err := model.DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			}).Preload("ModbusServerDB").Preload("OPCServerItem")
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Where("id = ? AND del = 0", param.Id).Limit(1).Find(&project).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	pageGroupList := make([]*model.PageGroup, 0)
	err = model.DB.Preload("PageList", func(db *gorm.DB) *gorm.DB {
		return db.Where("del = 0").Order("id ASC")
	}).Order("id ASC").Find(&pageGroupList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	userList := make([]*model.User, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&userList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	authRoleList := make([]*model.AuthRole, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&authRoleList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	authRoleList = append(authRoleList, &model.AuthRole{Id: 0})
	for _, role := range authRoleList {
		role.UserList = make([]*model.User, 0)
		for _, user := range userList {
			if user.Role == role.Id {
				role.UserList = append(role.UserList, user)
			}
		}
	}

	alertTypeList := make([]*model.AlertType, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&alertTypeList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}

	project.AuthRoleList = authRoleList
	project.PageGroupList = pageGroupList
	project.AlertTypeList = alertTypeList

	for _, ppe := range project.PPEList {
		if ppe.Code1 == "58" || ppe.Code1 == "61" {
			itemList := make([]*model.FrameItem, 0)
			err = model.DB.Where("ppe_id = ?", ppe.ID).Find(&itemList).Error
			if err != nil {
				ctx.JSON(200, gin.H{
					"code":    6,
					"message": err.Error(),
				})
				return
			}
			ppe.FrameItemList = itemList
		}
	}
	itemList := make([]*model.FrameItem, 0)
	err = model.DB.Where("project_id = ? AND code1 = ?", project.ID, "90").Find(&itemList).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	project.FrameItemList = itemList

	project.ApiURL = fmt.Sprintf("http://%s:%d/", project.ServerIP, project.ServerPort)
	addr := global.AppConfig.Section("websocket").Key("addr").String()
	project.WsURL = fmt.Sprintf("ws://%s%s/echo", project.ServerIP, addr)
	jsonB, _ := json.MarshalIndent(&project, "", "  ")
	projectDirName := fmt.Sprintf("tmp/%s", tools.GetNano())
	zipFileName := projectDirName + ".zip"
	jsonFileURL := fmt.Sprintf("%s/project.zt", projectDirName)
	jsonDestFile := filepath.Join(global.StaticPath, jsonFileURL)
	err = tools.WriteStringToFile(string(jsonB), jsonDestFile, "w")
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "WriteStringToFile error" + err.Error(),
			"data":    "",
		})
		return
	}
	err = tools.CopyDir(filepath.Join(global.StaticPath, fmt.Sprintf("upload/audio/%d", project.ID)), filepath.Join(global.StaticPath, projectDirName, "audio"))
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "copyDir error" + err.Error(),
			"data":    "",
		})
		return
	}
	zipDestFile := filepath.Join(global.StaticPath, zipFileName)
	err = tools.ZipCompressWithoutTopLevel(filepath.Join(global.StaticPath, projectDirName), zipDestFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "zipCompress error " + err.Error(),
			"data":    "",
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"url":  zipFileName,
			"name": project.Name,
		},
	})
}

/*ExportApp 生成测试端文件 --- */
func (con ProjectController) ExportApp(ctx *gin.Context) {
	param := struct {
		Id       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	ip, port := tools.GetIPPortFromHost(ctx.Request.Host)
	updateData := make(map[string]interface{})
	updateData["server_ip"] = ip
	updateData["server_port"] = port
	_ = model.DB.Model(&model.Project{}).Where("id = ?", param.Id).Updates(updateData).Error

	user := &model.User{}
	err = model.DB.Model(&model.User{}).Where("project_id = ? AND username = ? AND password= ?", param.Id, param.Username, tools.Md5(param.Password+LOGIN_KEY)).Limit(1).Find(&user).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if user.Id == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程账号密码不匹配",
		})
		return
	}

	project := model.Project{}
	err = model.DB.Where("id = ? AND del = 0", param.Id).Limit(1).Find(&project).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	if project.ServerIP == "" || project.ServerPort == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "请先配置工程IP和端口",
		})
		return
	}

	project.ApiURL = fmt.Sprintf("http://%s:%d/", project.ServerIP, project.ServerPort)
	addr := global.AppConfig.Section("websocket").Key("addr").String()
	project.WsURL = fmt.Sprintf("ws://%s%s/echo", project.ServerIP, addr)
	jsonB, _ := json.MarshalIndent(&project, "", "  ")
	destFile := filepath.Join(global.ClientAppPath, "config.json")
	err = tools.WriteStringToFile(string(jsonB), destFile, "w")
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件写入失败",
			"data": map[string]interface{}{
				"err": err.Error(),
			},
		})
		return
	}

	outputName := "tmp/client_app-" + tools.GetTimestamp() + ".zip"
	outputFile := filepath.Join(global.StaticPath, outputName)
	tools.CreateDir(filepath.Dir(outputFile))
	err = tools.ZipCompress(global.ClientAppPath, outputFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件压缩失败",
			"data":    err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"url":  outputName,
			"name": project.Name + "-" + time.Now().Format("060102"),
		},
	})
}

func (con ProjectController) ExportAppIn(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	project := model.Project{}
	err := model.DB.Where("id = ? AND del = 0", userToken.ProjectID).Limit(1).Find(&project).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if project.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的工程ID",
		})
		return
	}

	if project.ServerIP == "" || project.ServerPort == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "请先配置工程IP和端口",
		})
		return
	}

	project.ApiURL = fmt.Sprintf("http://%s:%d/", project.ServerIP, project.ServerPort)
	addr := global.AppConfig.Section("websocket").Key("addr").String()
	project.WsURL = fmt.Sprintf("ws://%s%s/echo", project.ServerIP, addr)
	jsonB, _ := json.MarshalIndent(&project, "", "  ")
	destFile := filepath.Join(global.ClientAppPath, "config.json")
	err = tools.WriteStringToFile(string(jsonB), destFile, "w")
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件写入失败",
			"data": map[string]interface{}{
				"err": err.Error(),
			},
		})
		return
	}

	outputName := "tmp/client_app-" + tools.GetTimestamp() + ".zip"
	outputFile := filepath.Join(global.StaticPath, outputName)
	tools.CreateDir(filepath.Dir(outputFile))
	err = tools.ZipCompress(global.ClientAppPath, outputFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件压缩失败",
			"data":    err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"url":  outputName,
			"name": project.Name + "-" + time.Now().Format("060102"),
		},
	})
}

// ImportCheck 导入工程 1.判断是否有同名的工程正在运行, 如果有则提示先停止工程再导入
func (con ProjectController) ImportCheck(ctx *gin.Context) {
	param := struct {
		File string `json:"file" binding:"required"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "请传入工程文件路径",
			"data":    err.Error(),
		})
		return
	}
	param.File = filepath.Join(global.StaticPath, param.File)
	exist, err := tools.FileExist(param.File)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "打开文件失败",
			"data":    param.File,
		})
		return
	}
	if !exist {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件不存在",
			"data":    param.File,
		})
		return
	}
	destDIR := filepath.Join(global.StaticPath, "tmp", tools.GetNano())
	err = tools.Unzip(param.File, destDIR)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "文件解压失败",
			"data": map[string]interface{}{
				"err": err.Error(),
			},
		})
		return
	}

	jsonFileContent, err := tools.ReadFileContent(filepath.Join(destDIR, "project.zt"))
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "读取工程文件失败",
			"data":    err.Error(),
		})
		return
	}
	project := &model.ProjectExt{}
	err = json.Unmarshal([]byte(jsonFileContent), &project)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "解析工程文件失败",
			"data":    err.Error(),
		})
		return
	}
	project.DestDIR = destDIR
	project.AllowActions = make([]string, 0)
	model.ImportingProjectExt = project
	projectExist := &model.Project{}
	err = model.DB.Where("name = ?", project.Name).Limit(1).Find(projectExist).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": err.Error(),
			"data":    "",
		})
		return
	}
	project.ProjectExist = projectExist
	if projectExist.ID != 0 {
		project.AllowActions = []string{"update", "cancel"}
		ctx.JSON(200, gin.H{
			"code":    0,
			"message": "当前工程名称存在, 可以覆盖或取消",
			"data": map[string]interface{}{
				"allow_action": []string{"update", "cancel"},
			},
		})
		return
	} else {
		project.AllowActions = []string{"insert", "cancel"}
		ctx.JSON(200, gin.H{
			"code":    0,
			"message": "当前工程名称不存在, 可以新增或取消",
			"data": map[string]interface{}{
				"allow_action": []string{"insert", "cancel"},
			},
		})
		return
	}
}

func (con ProjectController) ImportDo(ctx *gin.Context) {
	param := struct {
		Action string `json:"action" binding:"required,max=30"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": err.Error(),
			"data":    "",
		})
		return
	}
	if model.ImportingProjectExt == nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "请重新提交工程文件检查",
			"data":    "",
		})
		return
	}
	project := model.ImportingProjectExt // 导入的临时项目
	rawProjectID := project.ID           // 导入的临时项目ID, 注意和重名的旧工程ID不是一个值
	actionMatched := false
	for _, action := range project.AllowActions {
		if param.Action == action {
			actionMatched = true
			break
		}
	}
	if !actionMatched {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "当期操作不允许",
			"data":    "",
		})
		return
	}
	if model.ImportingProjectExt.Importing {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "正在导入中, 请勿重复导入",
			"data":    "",
		})
		return
	}
	model.ImportingProjectExt.Importing = true
	defer func() {
		model.ImportingProjectExt = nil
	}()
	switch param.Action {
	case "cancel":
		model.ImportingProjectExt = nil
		ctx.JSON(200, gin.H{
			"code":    0,
			"message": "success",
			"data":    "",
		})
		return
	case "update":
		if project.ProjectExist.ID == model.RunningProject.ID {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": "当前工程正在运行, 不允许覆盖",
				"data":    "",
			})
			return
		}
		err = model.DeleteProject(project.ProjectExist.ID) // 重名的旧工程ID
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": "删除旧工程失败",
				"data":    err.Error(),
			})
			return
		}
		//_ = os.RemoveAll(filepath.Join(global.StaticPath, fmt.Sprintf("upload/audio/%d", project.ProjectExist.ID))) // 重名的旧工程ID
	case "insert":
	default:
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "请传入正确的操作参数",
			"data":    "",
		})
		return
	}

	now := tools.GetNow()
	startTime := time.Now()
	project.ID = 0
	err = model.DB.Transaction(func(tx *gorm.DB) error {
		project.UID = 0
		project.CreateTime = &now
		project.UpdateTime = &now
		err = tx.Select("name", "desc", "server_name", "server_ip", "server_port", "time_ss", "redundant_enable", "redundant_name",
			"redundant_ip", "redundant_port", "redundant_time_ss", "create_time", "update_time", "api_url", "ws_url").Create(&project).Error
		if err != nil {
			return err
		}
		// 从这里开始 project.ID 已更新为导入的新工程ID
		for _, ppe := range project.PPEList {
			ppe.ID = 0
			ppe.ProjectID = project.ID
			project.CreateTime = &now
			project.UpdateTime = &now
			err = tx.Select("project_id", "code1", "code2", "code3", "protocol_name", "mode",
				"name", "config", "sort", "create_time", "update_time").Create(ppe).Error
			if err != nil {
				return err
			}
			for _, frame := range ppe.FrameList {
				frame.ID = 0
				frame.ProjectID = project.ID
				frame.PPEID = ppe.ID
				frame.CreateTime = &now
				frame.UpdateTime = &now
				err = tx.Select("project_id", "code1", "code2", "code3", "ppe_id", "name", "frame_type", "frame_mode",
					"frame_format", "frame_addr", "dec_type", "length", "send_type", "slave_id", "function_code", "db_number",
					"start_addr", "quantity", "data_mode", "json_detail", "data_content", "trigger_condition", "trigger_relation", "trigger_interval",
					"status", "trigger_var", "trigger_type", "related_frame_id", "create_time", "update_time").Create(frame).Error
				if err != nil {
					return err
				}
				itemList := make([]*model.FrameItem, 0)
				for _, item := range frame.FrameItem {
					item.ID = 0
					item.ProjectID = project.ID
					item.PPEID = ppe.ID
					item.FrameID = frame.ID
					item.CreateTime = &now
					item.UpdateTime = &now
					itemList = append(itemList, item)
				}
				if len(itemList) > 0 {
					err = tx.Create(itemList).Error
					if err != nil {
						return err
					}
				}
			}

			modbusDBList := make([]*model.ModbusServerDB, 0)
			for _, row := range ppe.ModbusServerDB {
				row.ID = 0
				row.ProjectID = project.ID
				row.PPEID = ppe.ID
				row.CreateTime = &now
				row.UpdateTime = &now
				modbusDBList = append(modbusDBList, row)
			}
			if len(modbusDBList) > 0 {
				err = tx.Select("*").Omit("id").Create(&modbusDBList).Error
				if err != nil {
					return err
				}
			}
			ociList := make([]*model.OPCServerItem, 0)
			for _, row := range ppe.OPCServerItem {
				row.ID = 0
				row.ProjectID = project.ID
				row.PPEID = ppe.ID
				ociList = append(ociList, row)
			}
			if len(ociList) > 0 {
				err = tx.Select("*").Omit("id").Create(&ociList).Error
				if err != nil {
					return err
				}
			}
			if (ppe.Code1 == "58" || ppe.Code1 == "61") && len(ppe.FrameItemList) > 0 {
				for _, item := range ppe.FrameItemList {
					item.ID = 0
					item.ProjectID = project.ID
					item.PPEID = ppe.ID
					item.CreateTime = &now
					item.UpdateTime = &now
				}
				err = tx.Select("*").Omit("id").Create(ppe.FrameItemList).Error
				if err != nil {
					return errors.New(fmt.Sprintf("debug---1 %d,%s", project.ID, err.Error()))
				}
			}
			j_log.LogInfo([]interface{}{"已导入第三方协议设备:", ppe.Name})
		}
		for _, fse := range project.FSEList {
			fse.ID = 0
			fse.ProjectID = project.ID
			project.CreateTime = &now
			project.UpdateTime = &now
			err = tx.Select("project_id", "name", "code1", "ip", "port", "redundant_enable",
				"redundant_ip", "redundant_port", "redundant_param1", "redundant_param2", "redundant_param3",
				"int_endian", "float_endian", "detail", "sort", "sync_rate", "stage", "create_time", "update_time").Create(fse).Error
			if err != nil {
				j_log.LogError([]interface{}{"debug --- 5", err})
				return err
			}
			for _, module := range fse.FSModule {
				module.ID = 0
				module.ProjectID = project.ID
				module.FSID = fse.ID
				module.CreateTime = &now
				module.UpdateTime = &now
				err = tx.Select("", "code1", "code2", "code3", "project_id", "fs_id", "module_type",
					"module_name", "json_detail", "create_time", "update_time").Create(module).Error
				if err != nil {
					j_log.LogError([]interface{}{"debug --- 6", err})
					return err
				}
				itemList := make([]*model.FrameItem, 0)
				for _, item := range module.FrameItem {
					item.ID = 0
					item.ProjectID = project.ID
					item.FSID = fse.ID
					item.ModuleID = module.ID
					item.CreateTime = &now
					item.UpdateTime = &now
					itemList = append(itemList, item)
				}
				if len(itemList) > 0 {
					err = tx.Create(itemList).Error
					if err != nil {
						return err
					}
				}
			}
			j_log.LogInfo([]interface{}{"已导入自研设备:", fse.Name})
		}
		for _, group := range project.PageGroupList {
			group.ID = 0
			group.ProjectID = project.ID
			group.CreateTime = &now
			group.UpdateTime = &now
			err = tx.Select("project_id", "group_name", "create_time", "update_time").Create(group).Error
			if err != nil {
				return err
			}
			pageList := make([]*model.Page, 0)
			for _, page := range group.PageList {
				page.ID = 0
				page.ProjectID = project.ID
				page.GroupID = group.ID
				page.CreateTime = &now
				page.UpdateTime = &now
				pageList = append(pageList, page)
			}
			if len(pageList) > 0 {
				err = tx.Select("project_id", "group_id", "page_attribute", "page_name", "content",
					"content_ceshi", "canvas", "exp_data", "seriesColors", "report_type", "is_default", "origin_id",
					"create_time", "update_time").Create(&pageList).Error
				if err != nil {
					return err
				}
			}
			j_log.LogInfo([]interface{}{"已导入画面分组:", group.GroupName})
		}
		uid := 0
		for _, role := range project.AuthRoleList {
			if role.Id != 0 {
				role.Id = 0
				role.ProjectID = project.ID
				role.CreateTime = &now
				role.UpdateTime = &now
				err = tx.Select("project_id", "name", "desc", "status", "route", "route_content",
					"route2", "route_content2", "page_report", "create_time", "update_time").Create(role).Error
				if err != nil {
					return err
				}
			}

			userList := make([]*model.User, 0)
			for _, user := range role.UserList {
				user.Id = 0
				user.ProjectID = project.ID
				user.Role = role.Id
				user.CreateTime = &now
				user.UpdateTime = &now
				if user.Role == 0 {
					err = tx.Select("project_id", "role", "tel", "username", "realname", "password", "gender",
						"age", "email", "avatar", "desc", "status", "login_type", "create_time", "update_time").Create(user).Error
					if err != nil {
						return err
					}
					uid = user.Id
				} else {
					userList = append(userList, user)
				}
			}
			if len(userList) > 0 {
				err = tx.Select("project_id", "role", "tel", "username", "realname", "password", "gender",
					"age", "email", "avatar", "desc", "status", "login_type", "create_time", "update_time").Create(userList).Error
				if err != nil {
					return err
				}
			}
			j_log.LogInfo([]interface{}{"已导入角色:", role.Name})
		}
		atList := make([]*model.AlertType, 0)
		for _, at := range project.AlertTypeList {
			at.ID = 0
			at.ProjectID = project.ID
			at.Audio = strings.ReplaceAll(at.Audio, fmt.Sprintf("/%d/", rawProjectID), fmt.Sprintf("/%d/", project.ID))
			atList = append(atList, at)
		}
		if len(atList) > 0 {
			err = tx.Create(atList).Error
			if err != nil {
				return err
			}
			j_log.LogInfo([]interface{}{"已导入报警设置信息:", len(atList)})
		}

		if len(project.FrameItemList) > 0 {
			for _, item := range project.FrameItemList {
				item.ID = 0
				item.ProjectID = project.ID
				item.CreateTime = &now
				item.UpdateTime = &now
			}
			err = tx.Select("*").Omit("id").Create(project.FrameItemList).Error
			if err != nil {
				return errors.New(fmt.Sprintf("debug---2 %d,%s", project.ID, err.Error()))
			}
			j_log.LogInfo([]interface{}{"已导入内部变量, 数量:", len(project.FrameItemList)})
		}

		err = tx.Model(&model.Project{}).Where("id = ?", project.ID).Update("uid", uid).Error
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "导入工程数据失败",
			"data":    err.Error(),
		})
		return
	}
	sourceAudioPath := filepath.Join(project.DestDIR, "audio")
	destAudioPath := filepath.Join(global.StaticPath, "upload/audio", fmt.Sprintf("%d", project.ID))
	err = tools.CopyDir(sourceAudioPath, destAudioPath)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "复制音频文件失败",
			"data":    err.Error(),
		})
		return
	}
	model.ImportingProjectExt = nil
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"detail": fmt.Sprintf("工程导入完成,总耗时%v", time.Since(startTime)),
		},
	})
}

func (con ProjectController) TimeSync(ctx *gin.Context) {
	id := ctx.Query("id")
	projectID, _ := strconv.Atoi(id)
	switch runtime.GOOS {
	case "linux":
	default:
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "NTP时间同步功能仅支持Linux系统",
			"data": map[string]interface{}{
				"linux": []string{
					"1. Linux系统必须安装服务systemd-timesyncd",
					"2. 配置文件 /etc/systemd/timesyncd.conf 需要有非root用户的写权限",
					"3. 麒麟系统需要关闭 KySec status",
				},
				"windows": "暂不支持",
			},
		})
		return
	}
	project := model.Project{}
	err := model.DB.Where("id = ?", projectID).First(&project).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "工程ID异常",
			"data":    err.Error(),
		})
		return
	}
	ntpConfigFile := "/etc/systemd/timesyncd.conf"
	ntpServerIP := ""
	RootDistanceMaxSec := "7200"

	if project.TimeSS == 1 {
		ntpServerIP = project.ServerIP
	} else if project.RedundantTimeSS == 1 {
		ntpServerIP = project.RedundantIP
	}

	if !tools.IsValidIP(ntpServerIP) {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "NTP服务器IP地址有误",
			"data":    "",
		})
		return
	}
	cfg, err := ini.Load(ntpConfigFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "加载配置文件失败 timesyncd.conf",
			"data":    err.Error(),
		})
		return
	}
	fmt.Printf("ntp_server_ip: %s\n", ntpServerIP)
	cfg.Section("Time").Key("NTP").SetValue(ntpServerIP)
	cfg.Section("Time").Key("RootDistanceMaxSec").SetValue(RootDistanceMaxSec)
	err = cfg.SaveTo(ntpConfigFile)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "保存配置文件失败 timesyncd.conf",
			"data":    err.Error(),
		})
		return
	}
	// 验证状态
	statusCmd := exec.Command("sudo", "systemctl", "restart", "systemd-timesyncd")
	output, err := statusCmd.Output()
	fmt.Println("sudo systemctl restart systemd-timesyncd:")
	fmt.Println(string(output))
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}
